package priv.pfz.raft.peer;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import priv.pfz.raft.common.SleepUtil;

import java.util.List;
import java.util.concurrent.Executors;

/**
 * 向所有节点发送消息，同步等待，大多数成功时返回成功，大多数失败或超时时返回失败
 *
 * @author pengfangzhou
 * @date 2022/2/27 2:02
 */
@Slf4j
@RequiredArgsConstructor
public class QuorumSender {

    public static void main(String[] args) {
        log.info("begin");

        int total = 5;
        int majority = total / 2 + 1;

        ConditionLatch<Boolean> conditionLatch = new ConditionLatch<>(
                resultList -> getFinalResult(resultList, majority) != null,
                Executors.newFixedThreadPool(total)
        );

        conditionLatch.submit(() -> doWorker(1, 5, false));
        conditionLatch.submit(() -> doWorker(2, 4, true));
        conditionLatch.submit(() -> doWorker(3, 3, false));
        conditionLatch.submit(() -> doWorker(4, 2, true));
        conditionLatch.submit(() -> doWorker(5, 1, true));

//        SleepUtil.sleep(8000);

        try {
            List<Boolean> results = conditionLatch.get(10000);
            Boolean finalResult = getFinalResult(results, majority);
            log.info("finalResult: {}", finalResult);
        } catch (Exception e) {
            log.error("", e);
        }
        SleepUtil.sleep(9999999);
    }

    private static Boolean getFinalResult(List<Boolean> resultList, int majority) {
        int successCount = 0;
        int failCount = 0;
        for (Boolean result : resultList) {
            if (result == null || !result) {
                failCount++;
            } else {
                successCount++;
            }
        }
        if (successCount >= majority) {
            return true;
        }
        if (failCount >= majority) {
            return false;
        }
        return null;
    }

    private static boolean doWorker(int index, long sleepSeconds, boolean result) {
        log.info("worker " + index + " begin");
        SleepUtil.sleep(sleepSeconds * 1000);
        log.info("worker " + index + " end, return " + result);
        return result;
    }
}
